package net.melon.sso.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import net.esj.basic.core.view.tools.ActionContext;
import net.esj.basic.exception.MelonException;
import net.esj.basic.exception.MelonRuntimeException;
import net.esj.basic.pojo.system.UserRule;
import net.esj.basic.service.provider.UserRuleProvider;
import net.esj.basic.utils.Base64;
import net.esj.basic.utils.DateUtils;
import net.esj.basic.utils.SecurityUtils;
import net.esj.basic.utils.UUIDCreator;
import net.melon.jdbc.JdbcDaoSupport;
import net.melon.jdbc.core.RowMapper;
import net.melon.sso.pojo.Config;
import net.melon.sso.pojo.Member;
import net.melon.sso.pojo.MemberType;
import net.melon.sso.pojo.WebSite;
import net.melon.sso.utils.PwdUtils;

import org.apache.commons.lang.StringUtils;


/**
 * @author Administrator
 *
 */
public class SSOService extends JdbcDaoSupport{
	
	private static final int DEFAULT_COOKIE_TIME = 2*60*60*1000;//两小时	
	
	public static final String COOKIE_UN = "__u_n_";
	
	private Map<String,Config> configCache = new HashMap<String, Config>();
	
	private static Map<String,Config> configKeyCache = new HashMap<String, Config>();
	
	private Map<String,WebSite> webSiteCache = new HashMap<String,WebSite>();
	
	@Resource
	private SSOManage ssoManage;
	
	@PostConstruct
	public void init(){
		loadConfig(false);
	}
	
	private static final String LOAD_CONFIG = "SELECT * FROM SSO_CONFIG ";
	
	public Map<String,Config> loadConfig(boolean reload){
		if(reload || configCache.isEmpty()){
			configCache.clear();
			configKeyCache.clear();
			getJdbcTemplate().query(LOAD_CONFIG, new RowMapper<Config>() {

				@Override
				public Config mapRow(ResultSet rs, int arg1)
						throws SQLException {
					Config c = new Config();
					c.setId(rs.getString("ID"));
					c.setKey(rs.getString("KEY"));
					c.setName(rs.getString("NAME"));
					c.setValue(rs.getString("VALUE"));
					c.setUsable(rs.getBoolean("USABLE"));
					c.setGroup(rs.getBoolean("GROUP"));
					configCache.put(c.getId(), c);
					configKeyCache.put(c.getKey(), c);
					return c;
				}
			});
		}
		return Collections.unmodifiableMap(configCache);
	}
	
	public static Config getConfigByKey(String key){
		return configKeyCache.get(key); 
	}
	
	public Config getConfig(String id){
		Config c = configCache.get(id);
		if(c==null){
			loadConfig(true);
			c = configCache.get(id);
		}
		return c;
	}
	
	private static final String CHECK_UNIQUE = "SELECT COUNT(1) FROM SSO_MEMBER WHERE  ";
	
	public boolean checkMemberUnique(String un){
		StringBuilder sb = new StringBuilder(CHECK_UNIQUE);
		if(net.esj.basic.utils.StringUtils.hasText(un)){
			sb.append(" MOBILE = '").append(un).append("'");
		}else{
			return false;
		}
		int c = getJdbcTemplate().queryForInt(sb.toString());
		if(c>0){
			return false;
		}
		return true;
	}
	
	private static final String LOAD_WEBSITE = "SELECT * FROM SSO_WEBSITE "; 
	
	public Collection<WebSite> loadWebsites(boolean reload){
		if(reload){
			webSiteCache.clear();
		}
		if(webSiteCache.isEmpty()){
			getJdbcTemplate().query(LOAD_WEBSITE, new RowMapper<WebSite>(){
				
				@Override
				public WebSite mapRow(ResultSet rs, int arg1) throws SQLException {
					WebSite site = new WebSite();
					site.setId(rs.getString("ID"));
					site.setName(rs.getString("NAME"));
					site.setDomain(rs.getString("DOMAIN"));
					site.setEncode(rs.getString("ENCODE"));
					site.setState(rs.getInt("STATE"));
					site.setPublicKey(rs.getString("PUBLIC_KEY"));
					site.setPrivateKey(rs.getString("PRIVATE_KEY"));
					site.setLogoutUrl(rs.getString("LOGOUT_URL"));
					site.setLoginUrl(rs.getString("LOGIN_URL"));
					site.setRegFlag(rs.getInt("REG_FLAG")==1);
					site.setEditpassFlag(rs.getInt("EDITPASS_FLAG")==1);
					webSiteCache.put(site.getDomain(), site);
					return null;
				}
			});
		}
		return Collections.unmodifiableCollection(webSiteCache.values());
	}
	
	public WebSite getWebSite(String id){
		if(webSiteCache.isEmpty()){
			loadWebsites(false);
		}
		for(WebSite site : webSiteCache.values()){
			if(StringUtils.equals(site.getId(), id)){
				return site;
			}
		}
		return null;
	}
	
	public Member reg(Member member) throws MelonException,Exception{
		if(!checkMemberUnique(member.getMobile())){
			throw new MelonException("err.unique.exists");
		}
		if(member.getType()==null|| !net.esj.basic.utils.StringUtils.hasText(member.getType().getId())){
			MemberType type = ssoManage.defaultRegisityMemberType();
			if(type==null){
				List<MemberType> list = ssoManage.listMemberType();
				type = list.get(0);
			}
			member.setType(type);
		}
		saveMember(member);
		return member;
	}
	
	

	private static final String INSERT_MEMBER = "INSERT INTO SSO_MEMBER (ID,PASSWORD,`TYPE`,GMT_CREATE,LAST_LOGIN_IP,LAST_LOGIN_TIME,REAL_NAME,MOBILE,ICON,STATE,AGENCY_ID) VALUES (?,?,?,?,?,?,?,?,?,?,?)";
	
	public Member saveMember(Member user){
		user.setId(UUIDCreator.create());
		user.setGmtCreate(DateUtils.now());
		user.setPassword(SecurityUtils.getMD5(user.getPassword()));
		getJdbcTemplate().update(INSERT_MEMBER , new Object[]{
				user.getId(),user.getPassword(), user.getType()==null?null:user.getType().getId(),user.getGmtCreate(),
				user.getLastLoginIp(),user.getLastLoginTime(),user.getRealname(),user.getMobile(),user.getIcon(),user.getState(),user.getAgencyId()},
				new int[]{Types.CHAR,Types.VARCHAR,Types.CHAR,Types.TIMESTAMP,Types.VARCHAR,
				Types.TIMESTAMP,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,Types.INTEGER,Types.VARCHAR});
		return user;
	}
	
	
	private static final String CONFIG_UNIQUE = "SELECT COUNT(1) FROM SSO_CONFIG WHERE ";
	
	public boolean checkConfigUnique(Config config, boolean isAdd){
		StringBuilder sb = new StringBuilder(CONFIG_UNIQUE);
		if(net.esj.basic.utils.StringUtils.hasText(config.getId()) && net.esj.basic.utils.StringUtils.hasText(config.getKey())){
			sb.append(" ID = '").append(config.getId()).append("' OR `KEY` = '").append(config.getKey()).append("'");
		}else{
			return false;
		}
		int c = getJdbcTemplate().queryForInt(sb.toString());
		if(!isAdd){
			if(c!=1){
				return false;
			}
		}else{
			if(c>0){
				return false;
			}
		}
		return true;
	}

	public Member normalLogin(String mobile, String password) {
		Member u = doFindUserInDB(mobile, password);
		if(u==null ){
			throw new MelonRuntimeException("reject.login");
		}
		if(!PwdUtils.checkPwd(u.getPassword(), password)){
			throw new MelonRuntimeException("reject.login");
		}
		if(u.getState()!=1){
			throw new MelonRuntimeException("reject.state.err");
		}
		//Member old = cloneMember(u);
		ActionContext.addCookie(COOKIE_UN, u.getMobile(),DEFAULT_COOKIE_TIME, false);
		ActionContext.getSession().setAttribute(getSessionKey(), u);
		ActionContext.getSession().setAttribute(COOKIE_UN, Base64.encodeToString(u.getMobile().getBytes(), false));
		u.setLastLoginIp(ActionContext.getClient().getIp());
		u.setLastLoginTime(DateUtils.now());
		updateLogin(u);
		return u;
	}
	
	public void logout(){
		ActionContext.getSession().removeAttribute(getSessionKey());
	}
	
	private static final String SELECT_BY_NAME_AND_PWD = "SELECT * FROM SSO_MEMBER WHERE MOBILE = ? ";
	
	private Member doFindUserInDB(String mobile,String password){
		List<Member> list = getJdbcTemplate().query(SELECT_BY_NAME_AND_PWD, new RowMapper<Member>(){

			@Override
			public Member mapRow(ResultSet rs, int arg1) throws SQLException {
				return loadMember(rs);
			}
		},new Object[]{mobile});
		if(list==null || list.isEmpty()){
			return null;
		}
		return list.get(0);
	}
	
	protected String getSessionKey(){
		UserRule rule = UserRuleProvider.getUserRule(Member.class);
		if(rule!=null){
			return rule.getSessionKey();
		}
		return null;
	}

	
	private static final String UPDATE_LOGIN = "UPDATE SSO_MEMBER SET LAST_LOGIN_IP = ? ,LAST_LOGIN_TIME = ?  WHERE ID = ? ";
	private void updateLogin(Member u){
		getJdbcTemplate().update(UPDATE_LOGIN, new Object[]{u.getLastLoginIp(),u.getLastLoginTime(),u.getId()});
	}
	
	
	private Member loadMember(ResultSet rs) throws SQLException{
		Member user = new Member();
		user.setId(rs.getString("ID"));
		user.setPassword(rs.getString("PASSWORD"));
		user.setLastLoginIp(rs.getString("Last_LOGIN_IP"));
		user.setGmtCreate(rs.getDate("gmt_create"));
		user.setLastLoginTime(rs.getDate("LAST_LOGIN_TIME"));
		user.setMobile(rs.getString("mobile"));
		user.setRealname(rs.getString("REAL_NAME"));
		user.setType(ssoManage.getMemberType(rs.getString("type")));
		user.setState(rs.getInt("state"));
		user.setIcon(rs.getString("ICON"));
		return user;
	}
	
	protected Member cloneMember(Member member){
		Member mm = new Member();
		mm.setId(member.getId());
		mm.setGmtCreate(member.getGmtCreate());
		mm.setLastLoginIp(member.getLastLoginIp());
		mm.setLastLoginTime(member.getLastLoginTime());
		mm.setMobile(member.getMobile());
		mm.setPassword(member.getPassword());
		mm.setRealname(member.getRealname());
		mm.setState(member.getState());
		mm.setType(member.getType());
		mm.setIcon(member.getIcon());
		return mm;
	}

	public void editPass(Member member, String pwd) {
		ssoManage.editPass(member,pwd);
	}

	
	private static final String UPDATE_PASS = "UPDATE SSO_MEMBER SET PASSWORD = ?   WHERE MOBILE = ? ";
	
	public void editPass(String un, String pwd) {
		StringBuilder sb = new StringBuilder(UPDATE_PASS);
		getJdbcTemplate().update(sb.toString(), new Object[]{SecurityUtils.getMD5(pwd),un});
	}
}
